package com.dhcc.service;

import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.dhcc.entity.IdGenerator;
import com.dhcc.mapper.IdGeneratorMapper;
import jakarta.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

/**
 * ID生成器服务类，负责ID的生成和状态管理。
 * @ProjectName Dhcc-Cloud
 * @PackageName com.dhcc.service
 * @Title IdGeneratorService
 * @Date 2024/10/17 下午7:26
 * @Author LiuGuoting
 */
@Service
public class IdGeneratorService {

    /**
     * Mapper用于数据库操作
     */
    private final IdGeneratorMapper idGeneratorMapper;

    /**
     * 上次生成ID的时间戳
     */
    private Long lastTimestamp;
    /**
     * 工作节点 ID
     */
    @Value("${id.worker-id}")
    private Long workerId;

    /**
     * 数据中心 ID
     */
    @Value("${id.datacenter-id}")
    private Long datacenterId;

    /**
     * 自定义纪元时间戳（2024-01-01），单位：毫秒。
     */
    private final long twepoch = 1704067200000L;

    /**
     * 机器 ID 所占的位数。
     */
    private final long workerIdBits = 5L;

    /**
     * 数据中心 ID 所占的位数。
     */
    private final long datacenterIdBits = 5L;

    /**
     * 序列号所占的位数。
     */
    private final long sequenceBits = 12L;

    /**
     * 支持的最大机器 ID，结果是 31（这个位移算法可以快速计算出最大值）。
     */
    private final long maxWorkerId = -1L ^ (-1L << workerIdBits);

    /**
     * 支持的最大数据中心 ID，结果是 31。
     */
    private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);

    /**
     * 机器 ID 左移位数（12位）。
     */
    private final long workerIdShift = sequenceBits;

    /**
     * 数据中心 ID 左移位数（12 + 5 位）。
     */
    private final long datacenterIdShift = sequenceBits + workerIdBits;

    /**
     * 时间戳左移位数（12 + 5 + 5 位）。
     */
    private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;

    /**
     * 生成序列的掩码，这里为 4095（0b111111111111=4095）。
     */
    private final long sequenceMask = -1L ^ (-1L << sequenceBits);

    /**
     * 毫秒内序列（0~4095）。
     */
    private long sequence = 0L;


    @Autowired
    public IdGeneratorService(IdGeneratorMapper idGeneratorMapper) {
        this.idGeneratorMapper = idGeneratorMapper;
    }

    /**
     * 初始化方法，在服务启动时加载数据库中的状态。
     */
    @PostConstruct
    public void init() {
        if (workerId > maxWorkerId || workerId < 0) {
            throw new IllegalArgumentException(String.format(
                    "工作 ID（workerId）不能大于 %d 或小于 0", maxWorkerId));
        }
        if (datacenterId > maxDatacenterId || datacenterId < 0) {
            throw new IllegalArgumentException(String.format(
                    "数据中心 ID（datacenterId）不能大于 %d 或小于 0", maxDatacenterId));
        }
        // 尝试从数据库中加载ID生成器的状态
        IdGenerator idGenerator = idGeneratorMapper.selectByWorkerIdAndDatacenterId(workerId, datacenterId);
        // 如果不存在，则初始化新的ID生成器状态
        if (idGenerator == null) {
            idGenerator = new IdGenerator();
            // 当前时间戳
            idGenerator.setLastTimestamp(System.currentTimeMillis());
            idGenerator.setWorkerId(workerId);
            idGenerator.setDatacenterId(datacenterId);
            // 插入新状态
            idGeneratorMapper.insert(idGenerator);
        }

        // 设置 lastTimestamp 为加载的状态
        lastTimestamp = idGenerator.getLastTimestamp();
    }

    /**
     * 生成下一个唯一ID。
     *
     * @return 返回生成的64位ID
     */
    public synchronized long nextId() {
        // 获取当前时间戳
        long timestamp = timeGen();

        // 如果当前时间小于上次生成 ID 的时间戳，说明系统时钟回退过
        if (timestamp < lastTimestamp) {
            throw new RuntimeException(String.format(
                    "时钟发生回退，拒绝在 %d 毫秒内生成 ID",
                    lastTimestamp - timestamp));
        }

        // 如果是在同一毫秒内生成的，则进行序列号递增
        if (lastTimestamp == timestamp) {
            sequence = (sequence + 1) & sequenceMask;
            // 毫秒内序列溢出
            if (sequence == 0) {
                // 阻塞到下一毫秒，获得新的时间戳
                timestamp = tilNextMillis(lastTimestamp);
            }
        } else {
            // 时间戳改变，序列号重置
            sequence = 0L;
        }

        // 更新lastTimestamp
        lastTimestamp = timestamp;

        // 保存新的状态到数据库
        // 更新 lastTimestamp 到数据库
        IdGenerator idGenerator = new IdGenerator();
        idGenerator.setLastTimestamp(lastTimestamp);
        // 创建条件构造器
        UpdateWrapper<IdGenerator> updateWrapper = new UpdateWrapper<>();
        updateWrapper.eq("worker_id", workerId)
                .eq("datacenter_id", datacenterId);
        // 执行更新
        idGeneratorMapper.update(idGenerator, updateWrapper);


        // 组合各部分生成最终的 ID，并返回
        return ((timestamp - twepoch) << timestampLeftShift)
                | (datacenterId << datacenterIdShift)
                | (workerId << workerIdShift)
                | sequence;
    }

    /**
     * 阻塞到下一个毫秒，直到获得新的时间戳。
     *
     * @param lastTimestamp 上次生成ID的时间戳
     * @return 返回当前时间戳
     */
    private long tilNextMillis(long lastTimestamp) {
        long timestamp = System.currentTimeMillis();
        while (timestamp <= lastTimestamp) {
            timestamp = System.currentTimeMillis();
        }
        return timestamp;
    }

    /**
     * 返回以毫秒为单位的当前时间。
     *
     * @return 当前时间（毫秒）
     */
    protected long timeGen() {
        return System.currentTimeMillis();
    }
}